package org.example.se.concurrent;


import java.util.Random;
import java.util.concurrent.*;

/**
 * CyclicBarrier 循环栅栏锁
 * 1、java.util.concurrent.CyclicBarrier 是一个同步辅助类，允许一组线程相互之间等待，达到一个共同点，再继续执行。可以理解为循环栅栏锁
 * 2、相比与 CountDownLatch，CyclicBarrier 有一个 reset() 方法可以将将屏障重置为其初始状态，即恢复构造器中最开始设置的个数值
 * 3、比如：8个运动员(线程)参加赛跑，必须得所有人都准备好了，才能开始比赛。再比如：游戏一方5个人(代表5个线程)组成团队赛，必须大家都准备好了，比赛才能开始
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/9/19 15:42
 */
public class CyclicBarrierTest2 {
    public static void main(String[] args) {
        int count = 5;
        //与 CountDownLatch 一样，初始化将来要同步的线程数要与实际的相等
        //当循环栅栏线程组中的线程都达到了栅栏后，会从中随机选一个来执行 new Runnable 任务
        //执行完后，循环栅栏组中的线程接着向后运行
        CyclicBarrier cyclicBarrier = new CyclicBarrier(count, new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "：玩家匹配完成...");
                    Thread.sleep(500 + new Random().nextInt(3000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("主线程调用开始，开始匹配玩家...");
        for (int i = 0; i < count; i++) {
            executorService.execute(new MyThread3(cyclicBarrier));
        }
        System.out.println("主线程调用完毕...");
    }
}

class MyThread3 implements Runnable {
    private CyclicBarrier cyclicBarrier;

    /**
     * 参数传入
     */
    public MyThread3(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(1 + new Random().nextInt(4));
            System.out.println("玩家 " + Thread.currentThread().getName() + " 加入游戏,开始等待其它玩家...");
            System.out.println("循环栅栏要求同步线程数:" + cyclicBarrier.getParties() + ",当前到达栅栏线程数:" + (cyclicBarrier.getNumberWaiting() + 1));
            //到达栅栏时，开始等待其它线程，一直到调用await方法的线程个数达到new CyclicBarrier(3)初始化的个数后
            //所有的线程才会接着向后运行，否则一直阻塞在这里
            cyclicBarrier.await();
            TimeUnit.SECONDS.sleep(1 + new Random().nextInt(4));
            System.out.println("玩家 " + Thread.currentThread().getName() + " 正在大杀特杀...");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

//---------------------控制台输出示例----------------------------
//主线程调用开始，开始匹配玩家...
//主线程调用完毕...
//玩家 pool-1-thread-4 加入游戏,开始等待其它玩家...
//循环栅栏要求同步线程数:5,当前到达栅栏线程数:1
//玩家 pool-1-thread-5 加入游戏,开始等待其它玩家...
//循环栅栏要求同步线程数:5,当前到达栅栏线程数:2
//玩家 pool-1-thread-1 加入游戏,开始等待其它玩家...
//玩家 pool-1-thread-3 加入游戏,开始等待其它玩家...
//循环栅栏要求同步线程数:5,当前到达栅栏线程数:3
//循环栅栏要求同步线程数:5,当前到达栅栏线程数:3
//玩家 pool-1-thread-2 加入游戏,开始等待其它玩家...
//循环栅栏要求同步线程数:5,当前到达栅栏线程数:5
//pool-1-thread-2：玩家匹配完成...
//玩家 pool-1-thread-4 正在大杀特杀...
//玩家 pool-1-thread-5 正在大杀特杀...
//玩家 pool-1-thread-3 正在大杀特杀...
//玩家 pool-1-thread-2 正在大杀特杀...
//玩家 pool-1-thread-1 正在大杀特杀...